#File: rondamon.py
#JGordon & Co

import sys
import ply.lex as lex
import ply.yacc as yacc

#--------------------------------------------------------------------------------
#Scanner definition

class RondamonException(Exception):
    pass

tokens = (
	'EQUAL', 'COMMA', 'NUMBER', 'ID', 'FU','STRING',
	'PLUS', 'MINUS', 'TIMES', 'DIV',
	'LPAREN', 'RPAREN', 'LBRAC', 'RBRAC',
	'LTHAN', 'GTHAN', 'PRINT', 'ISEQUAL', 'NOTEQUAL',
	'IF', 'THEN', 'ELSE', 'END', 'WHILE', 'DO',
	'AND', 'OR', 'RETURN','LOCAL','LENGTH','BREAK'
)

t_EQUAL     = r'[=]'
t_COMMA     = r','
t_PLUS      = r'[+]'
t_MINUS     = r'[-]'
t_TIMES     = r'[*]'
t_DIV       = r'[/]'
t_LTHAN     = r'<'
t_GTHAN     = r'>'
t_RPAREN    = r'[)]'
t_LPAREN    = r'[(]'
t_RBRAC     = r'\]'
t_LBRAC     = r'\['
t_ISEQUAL   = r'[=][=]'
t_NOTEQUAL  = r'~='
t_LENGTH    = r'[\#]'

reserved = {
	'end': 'END',
	'if': 'IF',
	'print': 'PRINT',
	'then': 'THEN',
	'else': 'ELSE',
	'fu' : 'FU',
	'print' : 'PRINT',
	'is' : 'IS',
	'not' : 'NOT',
	'while' : 'WHILE',
	'do' : 'DO',
	'and' : 'AND',
	'or' : 'OR',
	'return' : 'RETURN',
	'local' : 'LOCAL',
	'break' : 'BREAK',
}
def t_STRING(t):
    r'["][^"]*["]'
    #r'["][a-zA-Z0-9 \t]*["]'
    #t.value = t.value[1:-1]
    t.value = t.value[::]
    return t

def t_NUMBER(t):
    r'\d+'
    t.value = int(t.value)
    return t

t_ignore = ' \t'

def t_ID(t):
    r'[a-zA-Z]\w*'
    t.type = reserved.get(t.value, 'ID')
    return t

def t_comentario(t):
    r'\-\-.*'
    pass

def t_newline(t):
    r'\n+'
    t.lexer.lineno += t.value.count("\n")

def t_error(t):
    raise RondamonException("Illegal character '%s'" % t.value[0])

lex.lex()

#----------------------------------------------------------------------------------------
#Parser definition

root = None

def p_programa(p):
    '''programa : enunciados
    '''
    global raiz
    p[0] = [p[1]]
    raiz = p[0]

def p_vacio(p):
    'vacio :'
    pass
 
def p_enunciados(p):
    '''enunciados : vacio
                  | enunciado enunciados
    '''
    if len(p) == 2:
        p[0] = []
    else:
        p[0] = [p[1]] + p[2]

def p_enunciado(p):
    '''enunciado : enunciado_print
                 | enunciado_if
		 | enunciado_ifelse
                 | enunciado_assign 
		 | enunciado_while
		 | enunciado_return
		 | enunciado_fu
		 | enunciado_call
		 | enunciado_break
    '''
    p[0] = p[1]

def p_enunciado_print(p):
    'enunciado_print : PRINT expresion'
    p[0] = [p[1], p[2]]
    
def p_enunciado_break(p):
    'enunciado_break : BREAK'
    p[0] = [p[1]]

def p_enunciado_if(p):
    'enunciado_if : IF expresion THEN enunciados END'
    p[0] = [p[1], p[2], p[4]]
    
def p_enunciado_ifelse(p):
    'enunciado_ifelse : IF expresion THEN enunciados ELSE enunciados END'
    p[0] = [p[1]+p[5], p[2], p[4],p[6]]
    
def p_enunciado_while(p):
    'enunciado_while : WHILE expresion DO enunciados END'
    p[0] = [p[1], p[2],p[4]]

def p_enunciado_assign(p):
    'enunciado_assign : ID EQUAL expresion'
    p[0] = [p[2],p[1], p[3]]

def p_enunciado_return(p):
    'enunciado_return : RETURN expresion'
    p[0] = [p[1], p[2]]

def p_enunciado_fu(p):
    'enunciado_fu : FU ID LPAREN elementos RPAREN enunciados END'
    p[0] = [p[1], p[2],p[4],p[6]]

def p_enuciado_call(p):
    'enunciado_call : ID LPAREN elementos RPAREN'
    p[0] = ['call',p[1], p[3]]

def p_enuciado_index(p):
    'enunciado_index : ID LBRAC expresion_simple RBRAC'
    p[0] = ['index',p[1], p[3]]

def p_expresion(p):
    '''expresion : expresion_bool
    '''
    p[0] = p[1]

def p_expresion_bool(p):
    '''expresion_bool : expresion_menor
                      | expresion_bool AND expresion_menor
                      | expresion_bool OR expresion_menor
		      | expresion_bool ISEQUAL expresion_menor
		      | expresion_bool NOTEQUAL expresion_menor
    '''
    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = [p[2], p[1], p[3]]

def p_expresion_menor(p):
    '''expresion_menor : expresion_suma 
                       | expresion_menor LTHAN expresion_suma
		       | expresion_menor GTHAN expresion_suma
       expresion_suma  : expresion_mul 
                       | expresion_suma operador_suma expresion_mul
       expresion_mul   : expresion_simple 
                       | expresion_mul operador_mul expresion_simple
    '''
    if len(p) == 2:
        p[0] = p[1]
    else:
        p[0] = [p[2], p[1], p[3]]
        
def p_operador_suma(p):
    '''operador_suma : PLUS
                     | MINUS'''
    p[0] = p[1]

def p_operador_mul(p):
    '''operador_mul : TIMES
                     | DIV'''
    p[0] = p[1]

def p_expresion_simple(p):
    '''expresion_simple : enunciado_call
    			| enunciado_index
			| expresion_length
			| ID
                        | NUMBER
			| STRING
                        | MINUS expresion_simple
                        | LPAREN expresion RPAREN
			| LBRAC elementos RBRAC
    '''
    if len(p) == 2:
        p[0] = p[1]
    elif len(p) == 3:
        p[0] = [p[1],p[2]]
    #elif len(p) == 5:
    #    p[0] = [p[1], p[3]]
    else:
        p[0] = p[2]

def p_expresion_length(p):
    '''expresion_length : LENGTH LBRAC elementos RBRAC
    			 | LENGTH ID
    '''
    if len(p) == 3:
        p[0] = [p[1], p[2]]
    else:
        p[0] = [p[1], p[3]]

def p_elementos(p):
    '''elementos : vacio
             | expresion_simple mas_elementos
    '''
    if len(p) == 2:
        p[0] = []
    else:
        p[0] = [p[1]] + p[2]

def p_mas_elementos(p):
    '''mas_elementos : vacio
                     | COMMA expresion_simple mas_elementos
    '''
    if len(p) == 2:
        p[0] = []
    else:
        p[0] = [p[2]] + p[3]

def p_error(p):
    raise RondamonException(
        "Unexpected end of input" if p == None else
        "Syntax error at line %d near '%s'" % (p.lineno, p.value))

yacc.yacc()

#--------------------------------------------------------------------
# Facade for this module 

def parse(input):
    yacc.parse(input)
    return raiz 
